25

Git教程和常用命令

个人使用git的一些总结,一下常用命令的互相搭配使用,能非常完美的处理好日常遇到的99%的情况(不敢说100%,得留点余地)。
还有一些使用频率非常低的指令没有记录,至少普通开发者非常不常用,以后可能会补充。

话不多说
首先需要先下载git这个还是要说一下的

[TOC]


下面这一块内容请读者忽略,这里是我放的几条我个人常用的指令,免得往下找了,方便个人复制使用。
git config -l 只查看当前项目的git的配置
git config --global -l 查看全局git的配置
git remote prune origin 其他人删除线上某分支,自己这儿却还能看到,可以执行这个指令更新一下远程分支
git config branch.test.description '分支的注释' 用来给某个分支添加注释(注释信息只保存在本地,不能推送到远程仓库)
git commit --amend -m "<新注释>" 把当前的修改,补充提交到之前最后一次提交中(相当于回滚一次,添加当前修改后,重新用新注释提交)
git rev-list --count <分支名> 查看某个分支上面的提交数量,会打印一个数字


常用命令

最常用/版本对比/其他

git status 查看当前分支状态
git reflog 查看每一次的命令,都做过什么
git log 查看此分支完整的提交记录,回车继续看,q停止
git log --oneline 查看此分支简略的提交记录,只展示提交号和提交注释
git show 查看最近一次提交的具体代码变化
git show <提交ID> 查看某次提交的具体代码变化
git diff 查看当前代码add后,会add哪些内容
git diff --staged 查看现在commit提交后,会提交哪些内容
git diff HEAD 查看当前代码add并提交后,会提交哪些内容
git log --oneline --graph 图形化展示当前分支相关的所有历史提交
git log --oneline --graph --all 图形化展示所有分支的历史提交和分支关系
git log --oneline --graph --all --simplify-by-decoration 图形化展示所有的分支的相互合并关系

初始化基本操作

git init 初始化仓库,默认为master分支(最新版的git可能修改为了main分支)
git add -A 提交全部文件修改到缓存区
git add <具体某个文件路径+全名> 提交某些文件到缓存区
git commit -m "<注释>" 提交代码到本地仓库,并写提交注释
git commit --amend -m "<新注释>" 把当前的修改,补充提交到之前最后一次提交中(相当于回滚一次,添加当前修改后,重新用新注释提交)

git配置

git config -l 只查看当前项目的git的配置
git config --global -l 查看全局git的配置
git config <--global?> user.name 查看当前项目或全局的用户名
git config <--global?> user.email 查看当前项目或全局的用户邮箱
git config <--global?> user.name <用户名> 设置当前项目或全局的用户名
git config <--global?> user.email <邮箱> 设置当前项目或全局的用户邮箱

分支操作

git branch 查看本地所有分支
git branch -r 查看远程所有分支
git branch -a 查看本地和远程所有分支
git branch -v 查看分支和当前最新提交信息
git branch -vv 主要查看本地和远程分支的对应关系(就是使用 -u 绑定的分支)
git merge <分支名> 合并分支
git merge --abort 合并分支出现冲突时,取消合并,一切回到合并前的状态
git branch <新分支名> 基于当前分支,新建一个分支
git checkout --orphan <新分支名> 新建一个空分支(会保留之前分支的所有文件)
git branch -D <分支名> 删除本地某个分支
git push <远程库名> :<分支名> 删除远程某个分支
git branch <新分支名称> <提交ID> 从提交历史恢复某个删掉的某个分支
git branch -m <原分支名> <新分支名> 分支更名
git checkout <分支名> 切换到本地某个分支
git checkout <远程库名>/<分支名> 切换到线上某个分支
git checkout -b <新分支名> 把基于当前分支新建分支,并切换为这个分支
git remote prune <远程库名> 其他人删除线上某分支,自己这儿却还能看到,可以执行这个指令更新一下远程分支
git config branch.[分支名].description '分支的注释' 用来给某个分支添加注释(注释信息只保存在本地,不能推送到远程仓库)
git config branch.[分支名].description 查看某个分支的注释

对于分支注释查看,可以安装分支注释查看工具npm install -g git-br,然后使用git br来查看本地所有分支的注释

推拉操作(push和pull)

git pull <远程仓库名> <远程分支名> 拉取远程仓库的分支与本地当前分支合并
git pull <远程仓库名> <远程分支名>:<本地分支名> 拉取远程仓库的分支与本地某个分支合并
git push <远程仓库名> <本地分支名> 推送本地某个分支到远程与其对应的分支上,远程没有则会给远程库新建分支
git push <远程仓库名> <本地分支名>:<远程分支名> 推送本地分支到远程某个分支
git push <远程仓库名> :<远程分支名> 删除远程某个分支
git push -f <同上的命令> 强制提交,覆盖线上
git fetch 获取线上最新版信息记录,不合并,用法和pull类似(一个特殊的操作,比如多人开发分支,其他人提交后,自己看分支的时候,可能还知不道线上的分支已经比自己的新了,需要这个指令,来获取一下线上的最新版本,会拉取,但不会合并,存在本地仓库中)

查看具体文件版本区别(git diff)

git diff <文件名> 对比最近提交,和近近次提交的区别,不加文件名,则为整体对比
git diff HEAD^ -- <文件名> 同上
git diff HEAD~<一个数字> -- <文件名> 上次提交和前第几次提交作对比
git diff <提交ID> <文件名> 上次提交和某次提交作对比
git diff <文件名> 工作区与暂存区比较
git diff HEAD <文件名> 工作区与最近提交比较
git diff <本地分支名> <文件名> 当前分支的文件与其他分支的文件进行比较

回滚操作 reset

真实硬性回滚,回到目标版本,比目标版本更新的提交记录,会全部丢失掉,操作和文件是否会丢失,取决于是否使用了--hard

  • --hard:加上他,文件修改会被删除,丢失掉;不加他,文件修改会保留,都会处于add之前的状态。
这个指令可以用来把多次提交合并成一次。
比如小明切出一个新的功能分支,然后花了十次提交,开发完了一个很小的功能,但是感觉十次提交太浪费,会被同事看不起。
那么可以直接git reset <开始开发前的最后一次提交>,然后git add -A + git add -m "开发A功能" + git push origin -f <分支名称>,一套下来,神不知鬼不觉的,十次提交被压缩成了一次提交。。。当然如果这个功能分支已经被合并到主开发分支,或者被别人拉取过了,最好就别整了,那就变成欲盖弥彰了。。。

git reset <--hard?> HEAD^ 回退到上次提交
git reset <--hard?> <提交记录> 回退到之前的某次提交
git reset <--hard?> <某个分支> 回退到此分支的提交状态,相当于复制分支过来
git reset <文件完整路径+完整名>add过的某个文件撤销到未add的状态
git resetadd过的所有文件撤销到未add的状态
git checkout <文件完整路径+完整名> 一个还没有add的文件,撤销修改
git checkout . 还没有add的所有文件,撤销修改
git checkout -f 撤销当前所有未提交的修改
git commit --amend 修改commit提交后,可使用此指令进入vim编辑器修改commit信息

重做 revert

比如小明正在和很多同事,一起开发一个项目。
这天,小明受命开发A功能,对很多文件进行了修改,开发完成后,就进行了提交'A功能开发',并且推送到远程仓库,其他同事们也进行了拉取。
可是,同事刚刚拉取完,领导突然说,A功能不要了,撤回去吧。
这时候,小明可以使用reset进行回滚,然后硬性覆盖git push -f提交,再通知其他所有拉取过'A功能开发'的同事,把本地代码回滚一下。
但这样操作实在低端,尤其当同事拉取了你的代码后,已经再开发、甚至已经有了自己的提交时,在进行回滚就比较烦人了,而且如果都过去一周了,领导再要求撤回去,那就更恶心人了。
所以这里可以使用revert

git revert <提交记录>当执行这个指令时,git会先判断<提交记录>这次提交,用户对项目做了哪些修改,然后把用户做的修改再改回去,合并到当前(没看懂这行的,请仔细多阅读几遍)。

下面用例子进行解释

当我执行git revert <提交记录one>,等效于:
我请一个名叫小强的工人,他执行git checkout -b temp,把当前这个分支切出一个名为temp的新分支。
小强先执行git reset --hard <提交记录one>回到那次提交记录,接着,就问我这次记录我都修改了什么,他就一点点的改回去,没错,就是「手工回滚」。
修改完成后,他就提交了,并且通知我合并temp这个分支。
于是我就合并了,就是我执行git revert <提交记录one>后看到的情况。
可能没有冲突,我高高兴兴合并了;也可能有严重冲突,比如小强「手工回滚」改回的代码里面,有我后面用到的,这时候我就要处理冲突了。
当我处理完成,进行add,然后commit提交,这次revert就完成了。

此时,我的分支里面只多了一次提交(如果真按我上面的流程走,应该是两条,一条是小强在temp分支的那个提交,一个是我合并temp分支的提交,但这里使用revert,只有一次提交)。

新的提交,对以前的提交不会产生一点影响,这个时候,小明就可以看做是完成了一个新的功能---'去掉A功能',然后正常push就行啦,大家皆大欢喜。

git revert HEAD 撤回当前最近的这次提交的修改
git revert <提交记录> 撤回<提交记录>那次的提交的修改

远程库和分支的链接操作

git remote -v 查看对应的远程仓库地址
git clone <远程仓库地址链接> 克隆某个远程库,默认库名为origin
git clone <远程仓库地址链接> <库名> 克隆某个远程库,并自定库名
git remote remove <远程仓库名> 和远程的某个仓库解除绑定关系
git remote add <远程仓库名> <远程仓库地址链接> 和某个远程仓库建立绑定关系
git remote set-url <远程仓库名> <要改为的远程仓库地址链接> 修改绑定的远程库链接
git push --set-upstream <远程仓库名> <远程分支名> 当前分支和远程某个分支建立绑定关系

储藏操作(stash)

经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态。
而你想转到其他分支上进行一些工作。
问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。
解决这个问题的办法就是git stash命令。

git stash save <注释信息> 当前分支提交到储藏,插到储藏序列的最前面(不包括新建文件)
git stash save -u <注释信息> 同上,会包括新建文件,功能更强大
git stash list 查看所有储藏中的工作
git stash drop <储藏的名称> 删除对应的某个储藏
git stash pop 取出储藏中最后存入的工作状态进行恢复,会删除储藏
git stash pop <储藏对应的数字> 取出储藏中对应的工作状态进行恢复,会删除储藏
git stash apply 取出储藏中最后存入的工作状态进行恢复,注意,不会删除储藏
git stash apply <储藏的名称> 取出储藏中对应的工作状态进行恢复,注意,不会删除储藏
git stash branch <新分支名> <储藏的名称> 从储藏中新建分支,会删除储藏
git stash clear 清空所有储藏中的工作

标签操作(tag)

tag 是什么,虽然整个开发过程,每个提交都对应一个提交ID,回滚也比较方便,但总有一些重大意义的提交,比如从v0.91升级到了v1.0,这个v1.0是必须要单独摘出来保存的,否则万一出现回滚事故,导致提交记录丢失呢?

虽然可以单独新建分支保存,但分支的功能并不是用来做这个的。冗余的分支在开发时也容易产生误操作。

所以tag的用处就来了,他可以把某次提交后的项目,整个单独复制拎出来,命名为一个tag(可以以项目版本或打tag的时间来命名),可以写注释,存在tag列表中,与分支隔离开,除非专门命令操作,否则只要项目存在,他们就不会受到影响。

同时,可以使用tag快速回滚项目、基于tag新建分支等,在项目整体的管理上非常方便。

git tag 列出本仓库中所有tag列表
git tag <tag名>' 基于最近的一次提交打tag(轻量tag,不推荐)
git tag -a <tag名> -m '<此tag的注释>' 基于最近的一次提交打tag
git tag -a <tag名> <提交ID> -m '<此tag的注释>' 基于某次提交打tag
git show <tag名> 查看此tag的tag信息
git tag -d <tag名> 删除本地某个tag
git push origin :refs/tags/<tag名> 删除远程仓库中的tag
git push <远程仓库名> <tag名>' 推送某个tag至某个远程库
git push <远程仓库名> --tags' 推送所有tag至某个远程库
git checkout -b <新分支名> <tag名>' 基于某个tag新建分支

git查看分支提交历史:log指令

查看分支提交历史,也可以通过参数,查看更复杂的提交、分支相互合并、分支存亡历史等。

git log直接指示当前分支上的相关提交历史,关键就是他的参数,下面这些参数都可以组合使用:

--oneline # 【常用】省略大部分提交信息,只展示提交版本号 + 提交信息,很常用
--graph # 【关键】图形化展示这个分支相关的所有提交的来源
--simplify-by-decoration # 只展示合并信息,其他提交信息都忽略不显示
--decorate # 据说是用来可以显示出指向提交的指针的名字,也就是HEAD指针,但我实测貌似没有任何效果

如果只是想查看当前分支的提交记录,推荐使用下面的指令,它会删去一些平时不用关注的提交时间、提交者等信息。

git log --oneline

如果查看更复杂的分支信息,就需要用到关键参数「--graph」了。

这个功能是我公司突然切换主战项目时,回到久不开发的项目,看到一大堆分支时,懵逼的我无奈开始研究的,建议没有特殊需要的,也不必往下看了,毕竟算是很不常用了。

先说一下,git自带一个图形界面,可以直接在git仓库文件夹下,执行gitk,就可以调出一个图形界面,展示的形式和下面类似,只是会有更多附加信息,可以参考。

它主要有以下相关指令:

git log --oneline --graph 图形化展示当前分支相关的所有历史提交
git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit 图形化展示当前分支相关的所有历史提交(带提交者和提交距今时间)
git log --color --graph --pretty=format:'%C(bold white)%h%Creset -%C(bold green)%d%Creset %s %C(bold green)(%cr)%Creset %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative 图形化展示当前分支相关的所有历史提交(带提交者和提交距今时间)
git log --oneline --graph --all 图形化展示所有分支的历史提交和分支关系
git log --oneline --graph --all --simplify-by-decoration 图形化展示所有的分支的相互合并关系

它图形化展示这个分支相关的所有提交的来源,当使用以下指令时,会展示一个彩色的线和字组合的图形。

图形会很难理解,虽然乍一看都能看懂,看上去只是用线把一次次的提交,指向这里,又指向那里,一根线分叉为两个,两根线合并为一根。

这里大致说明几点:

  • 图需要从下往上看,注意观看顺序。
  • 这个图只是「提交」的去向图,和分支无关,分支只是用来承载「提交」的载体。
  • 图的左侧,是用线和星号「*」展示的提交的分叉和合并,可以理解为铁路和站台。
  • 一个星号代表一次提交,它右侧的字是这次提交的说明,且同一水平线上,只会同时有一个星号。

下面是例子+说明。

git-graph.png

rebase 变基功能

rebase 变基功能,可以使项目中的提交历史干净整洁。

比如:

  • 同一分支时:两个人同时拉取了某个分支的最新提交,各自开始开发并提交,最后合并代码时,这个分支时将出现无意义的分叉。
  • 合并分支时:基于 dev 分支,checkout 了一个 dev2 分支,接着两个分支各自有了新的提交,最后把 dev2 合并到 dev 时,将出现无意义的分叉。

同一分支时

有开发者小明和大黄,小明初始化了项目,把项目推送到线上;然后大黄拉取项目,提交两次后 push 项目。

在大黄开发时,小明也提交了一个功能,push 前拉取项目,发现大黄有提交,则小明先 pull 拉取一下。

此时可能无冲突或者有冲突,如果没有冲突,则出现以下提示:

Snipaste_2021-02-18_16-30-02.png

此时直接正常操作,编辑合并记录,或者直接使用默认合并提交即可(依次按下 : wq 回车)。

如果有冲突,则会出现以下提示,需要处理后再 add,最后提交:
Snipaste_2021-02-18_20-56-21.png

无论是否有冲突,处理完成后,此时再查看提交记录:

Snipaste_2021-02-18_16-30-15.png

会发现提交记录分叉了。。。但实际我们不想要这个分叉,复杂了提交记录,不便于阅读。

此时我们可以直接输入 git rebase 回车。

如果之前拉取时没有冲突,则随后控制台提示:

Successfully rebased and updated refs/heads/master.

说明变基成功,再查看提交记录:
Snipaste_2021-02-18_16-30-20.png

相当于 git 帮我们把提交记录进行重新排列:回到 小明初始化 这个提交状态,拉取大黄的两条记录,补上小明的提交。

如果之前拉取时有冲突,则这里会麻烦一些,但原理和上面其实一样:回到 小明初始化 这个提交状态,拉取大黄的两条记录,补上小明的提交。。。不同的是这里「补上小明的提交」这一步,因为是有冲突的,所以这时又会出现冲突,需要再人工处理一遍。。。

确认冲突处理好之后,执行 git add -a,然后再按照提示输入 git rebase --continue,再写一下新的提交记录,即可。

如果处理冲突时,不想 rebase 了,则随时在控制台输入 git rebase --abort,则可以退出 rebase,返回 rebase 之前的状态。

完成 rebase 后,虽然改变了提交和提交版本,但此时我们还没有提交,完全不影响线上,还把提交记录归一了,清晰明了了许多。

合并分支时

这个故事只有小明,小明这天接了个任务,于是基于 master 分支 checkout 了一个 dev 分支,在 dev 开始开发。

这时 master 分支出了一些 bug(或者合并了其他的分支),有了一些新的提交。

终于,dev 分支的功能开发完成,但如果此时直接把 dev 分支合并到 master,那后果可像而知,必然又将劈叉了,其结果如下所示:

Snipaste_2021-02-18_21-54-50.png

这当然不行了,我们不想要劈叉的提交历史。

所以,回到 master 合并 dev 之前,使 master 回到 af08311 这个状态。

然后切换到 dev 分支,先看一下当前 dev 的提交记录:

Snipaste_2021-02-18_21-59-17.png

可以知道,init 这个提交是发生在 master 分支的,后面两个提交,是 checkout 到 dev 分支后提交的。

然后开始执行变基:

git rebase master

执行后,如果 dev 的新内容,和 master 当前内容无冲突,则会直接打印:

Successfully rebased and updated refs/heads/dev.

此时再查看 dev 的提交记录:
Snipaste_2021-02-18_21-58-13.png

发现,在 dev 分支的提交记录变了,master 新的那两个提交,也出现在 dev 分支前面,虽然提交的版本号还是变了(肯定还和 dev 分支的远程仓库分支的提交不同了),但关键是,此时再把 dev 合并到 master 分支,是不会再劈叉的了,而且,dev 分支作为功能分支,本身是不太重要的,完全可以把 dev 分支,覆盖推送一下。

出现冲突

上面是执行 git rebase master 后无冲突时的情况,顺风顺水,而当有冲突的时候,就麻烦了。

比如我们在 dev 分支添加两条我们确定会有冲突的修改并提交,再进行 rebase。

这里其实也就暴露了 rebase 的比较细的过程:rebase 过程中,会把 dev 分支的新提交们,一个个拿出来和 master 对比,如果无冲突,直接无感合并。

但如果发现某个提交有冲突,rebase 过程会卡住,然后会让你处理冲突,这个提交的冲突处理完成,手动执行一下 git add -A(控制台会有提示,也会提示终止 rebase 的指令),然后按照提示再执行 git rebase --continue,则此条提交处理完成,再拿出 dev 分支的下面一条提交,继续判断,直到所有提交处理完成。

最终处理完成后,dev 分支的每条提交都会保留,当然都是处理完冲突的:

Snipaste_2021-02-18_22-15-09.png

也就完成了 rebase 的使命。

.gitignore文件

在使用Git的过程中,有的文件比如日志、临时文件、编译的中间文件等不要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交。

没错就是.gitignore文件了。

此文件需要放在.git(默认是隐藏文件,是git的本地仓库)同一目录下

写法如下:

.DS_Store
.parcel-cache
.cache
node_modules
dist
dist-ssr
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.local

这样,当git add的时候,上面定义忽略的那些文件,就会被git忽略,发生了变化git也不会管。

然而有时候,是会出现意外的。

比如,git已经提交过了,这是后突然说,这里面有个文件需要被忽略,然后我把这个文件写到了.gitignore文件中。。。无效!这个文件仍然没有被忽略!

这是就需要如下命令,来删除此文件并忽略

# 在git库中删除文件并停止追踪,但实际文件还保留,只是忽略掉。
git rm -r --cached <文件路径+文件全名>

由此,方可。

git 给指令设置别名

类似于Linux系统的alias别名功能,git也可以使用它,给git指令设置别名。

这里只举一个例子,比如下面的指令,虽然很好用,但是实在太长了,想要使用只能靠复制:

# 图形化展示当前分支相关的所有历史提交(带提交者和提交距今时间)
git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

所以这里,可以使用git的alias功能,给他设置为git的全局一个'别名',相当于给git再人工拓展一条指令,直接输入以下:

git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

然后再输入:

git lg

此时,git lg的效果,和那老长一条的指令的效果一模一样。

其他如果有想缩减的git指令,可以同理设置。

使用多套账户和密码时的区分

以下方法仅适用于只使用账户和密码时的情况,其实更推荐使用 SSH 方案,更方便且安全。

注意下面这个方法,需要确保github登录密码只是字母、数字、下划线、横杠等等,如果有其他特殊字符,不确保能否成功,因为需要把密码写在url中,可能密码中的一些特殊字符,url不支持,也就出问题了。

有时会有这样的需求,一个项目,需要推送到github的两个账户的两个仓库下面。

比如自己项目默认的远程分支是:

仓库1
仓库名称1:origin_1
仓库地址1:https://github.com/account_1/item.git
账户名1:account_1
密码1:passwork_1

这是上面通知,需要同时把项目往下面一个账户上推送

仓库2
仓库名称2:origin_2
仓库地址2:https://github.com/account_2/item_share.git
账户名2:account_2
密码2:passwork_2

方法一(笨方法,明白原理的可以直接看方法二):

先添加仓库:

git remote add origin_2 https://github.com/account_2/item_share.git

再进行提交:

git push origin_2 master

那这里会报错无权限,因为此时电脑中记住的github账户名密码是仓库1的那一套。

这时可以通过修改系统记住的github账户,改成仓库2的,但这样比较麻烦,每次推送都需要来回修改。

【解决办法】:

修改当前本地项目的git配置,文件为当前项目地址下的.git/config,可以先查看一下当前配置:cat .git/config

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        precomposeunicode = true
[remote "origin_1"]
        url = https://github.com/account_1/item.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master

这里修改此文件,添加下面这一段

[remote "origin_2"]
        url = https://账户名2:密码2@github.com/account_2/item_share.git
        fetch = +refs/heads/*:refs/remotes/origin/*

保存后,再进行推送git push origin_2 master就可以了,自此推送origin时,自动使用全局保存的账户和密码,推送origin_2时,自动使用这里配置的账户名和密码。

方法二:

可以在添加仓库的时候,直接在仓库地址中加上账户名和密码:

git remote add origin_2 https://账户名2:密码2@github.com/account_2/item_share.git

上一步可能报错类似 -bash: ... event not found,这是因为 clone 的这个地址,实际是一个 url,但给 url 添加的账户名或密码,格式中可能包含 url 无法解析的字符。

如果有这种情况,那就老实用方法一吧。

然后再进行提交:

git push origin_2 master

可以直接成功,完成。

使用 SSH

绑定一个远程仓库,有两种方式:

  • 使用http链接的方式
  • 使用ssh加密链接的方式

如果远程仓库为公开仓库,则两者在使用上,区别不大;

但如果是私密仓库,http 方式是需要连接远程仓库时输入用户名和密码,这就导致账号和密码容易暴露,因为任何人在任何设备上,都可以使用这套账号和密码连接仓库,不够安全,且当同一台电脑,需要开发不同远程仓库的项目时,可能还会搞混账户和密码。

所以就有了下面更合适的方式 ssh

ssh 只需要本地设备使用指令生成一对公钥和私钥,然后把公钥的内容,复制添加到远程库的设置中,让远程库认识此设备,再复制项目的 ssh 链接,来绑定这个远程仓库,这种方法,远程仓库会只认设备不认账户,只要保证设备是安全的,仓库就是安全的,就算设备丢失,也不需要更换密码,只用登录远程仓库页面,删除这个设备的公钥即可。

此外,一个设备能生成多对公钥和私钥,通过 ssh 的 config 文件来管理,能够轻松解决不同远程仓库的问题。

基础使用 SSH

# 默认生成一对名为 id_rsa 的公钥和私钥:公钥名:id_rsa.pub 私钥名:id_rsa
ssh-keygen

# 生成指定名称的 公钥私钥
ssh-keygen -f id_new # 生成一对名为 id_new 的公钥和私钥

不同系统存放的默认存放位置:

  • windows:C盘/用户/<用户名称>/.ssh
  • MacOs:~/.ssh
  • Linux:/root/.ssh

.ssh 文件夹中,有两种格式的文件:

  • .pub 格式为公钥,需要打开复制其中的内容,粘贴到需要的git远程仓库网站的账户设置中。
  • 同名的无后缀名的为这个公钥对应的私钥,不要动他,也不要发给其他任何人,放在这个位置保管好。

注:一个ssh钥匙对,在一个git远程仓库网站中,只能被一个用户添加。

如果有另一个用户,也想把仓库共享给这台设备,再次写入公钥,则会失败。

注意,当添加完ssh,本地拉取github代码时,可能会出现下面的提示

# 输入
git clone git@github.com:***/***.git

# 输出
Cloning into '***'...
The authenticity of host 'github.com (13.229.188.59)' can't be established.
RSA key fingerprint is SHA256:***.
Are you sure you want to continue connecting (yes/no)?

注意这里,一定要输入 yes 再回车,否则会报错。

上面虽然只生成了一套秘钥,但可以把公钥同时配置到不同网站中,比如 github、gitee、gitlab 等等,当多个不同网站的 git 项目进行推拉时,虽然都会使用同一份秘钥,但其实也都可以正常生效。

同一远程仓库下的多个账户

场景:同一个远程仓库,比如都是 github 的项目,但是有多个账户,A 项目在 A 账户,B 项目在 B 账户中。

比较大力无脑的方法,就是照样还是同一个公钥,分别配置到账户 A 和 账户 B 的配置中,依然可以使用。

但有时候我们就是需要区分一下,给两个账户使用两套 ssh。

回到生成 ssh 之前,先分别生成多个 SSH 钥匙对:

# A 项目的钥匙对
ssh-keygen -f id_A

# B 项目的钥匙对
ssh-keygen -f id_B

生成完毕后,进入对应存放ssh钥匙对的.ssh文件夹中,会发现多两对秘钥:

# SSH 的文件夹中

# A 项目的秘钥对文件
id_A
id_A.pub

# B 项目的秘钥对文件
id_B
id_B.pub

下面开始配置,分别登陆两个账户,找到设置 SSH 的页面,把 A 账户的公钥内容设置到 A 账户中,B 账户的公钥内容设置到 B 账户中。

然后,可以拿到 github 中的用户 A 和用户 B 对应的两个项目的项目的 ssh 链接:

git@github.com:account-a/item-a.git
git@github.com:account-b/item-b.git

但这里我们不能再直接使用了,因为两个的域名完全一样,ssh 无法区分两者了,所以需要把连接修改一下备用:

git@a.github.com:account-a/item-a.git
git@b.github.com:account-b/item-b.git

接着,打开开发电脑的 ~/.ssh/config 文件(如果没有这个文件,就创建这个文件)配置一下,让我们在拉取项目时,ssh 能使用对应的正确的秘钥,内容如下:

Host a.github.com # 域名拦截
  HostName github.com # 拦截成功后,重新指向的域名,这里需要写正确的
  User git
  IdentityFile ~/.ssh/id_A
  PreferredAuthentications publickey

Host b.github.com # 域名拦截
  HostName github.com # 拦截成功后,重新指向的域名,这里需要写正确的
  User git
  IdentityFile ~/.ssh/id_B
  PreferredAuthentications publickey

此时,设备可以成功绑定这两个账户的仓库,可以输入一下指令,来验证ssh校验是否通过:

ssh -T a.github.com

# 出现以下打印,说明成功
# Hi user-B! You've successfully authenticated, but GitHub does not provide shell access.

之后,就可以各自去执行克隆指令,拉取项目,各自开发,各自推送,互不影响又各自正常运行,这里记得要用修改过的链接:

# 克隆 A 账户的 A 项目
git clone git@a.github.com:account-a/item-a.git

# 克隆 B 账户的 B 项目
git clone git@b.github.com:account-b/item-b.git

依次类推,设备绑定多个账户,不再是问题,关键就是配置config文件,能够对应起来即可。

拓展提示

可以发现,我们修改后的域名和 .ssh/config 中配置的 Host 域名是对应的:

# 修改后的域名为 a.github.com
git@a.github.com:account-a/item-a.git

# .ssh/config 中的 Host 也为 a.github.com
Host a.github.com # 域名拦截,仓库域名
  HostName github.com # 拦截成功后,重新指向的域名,这里需要写正确的,可以设置为 ip
  ...

其实,我们这里的域名可以随意设置,只要两者能对应,再把 HostName 配置正确就OK,比如如下的配置也是可行的:

# 修改后的域名为 my-item
git@my-item:account-a/item-a.git

# .ssh/config 中的 Host 也为 my-item
Host my-item
  HostName 132.132.132.132 # 这里也可以设置为 ip
  ...

Git安装完成后的设置

用户名、邮箱、登录账户

git config -l 只查看当前项目的git的配置
git config --global -l 查看全局git的配置
git config <--global?> user.name 查看当前项目或全局的用户名
git config <--global?> user.email 查看当前项目或全局的用户邮箱
git config <--global?> user.name <用户名> 设置当前项目或全局的用户名
git config <--global?> user.email <邮箱> 设置当前项目或全局的用户邮箱

修改用户登录信息

  • window
    控制面板 -> 用户账户 -> 管理 Windows 凭据,即可看到普通凭据中有git的账户信息,可编辑更改
  • Mac
    钥匙串访问.app -> 右上角搜索git,即可看到,可编辑更改

在比较新的git版本拉取代码时,会出现一下提示:

warning: Pulling without specifying how to reconcile divergent branches is
discouraged. You can squelch this message by running one of the following
commands sometime before your next pull:

  git config pull.rebase false  # merge (the default strategy)
  git config pull.rebase true   # rebase
  git config pull.ff only       # fast-forward only

You can replace "git config" with "git config --global" to set a default
preference for all repositories. You can also pass --rebase, --no-rebase,
or --ff-only on the command line to override the configured default per
invocation.

Already up to date.

这个提示在任何项目的每次下拉,都会出现,想要解决,就需要按照他的提示进行设置,可以直接在控制台输入一下指令,直接回车,就设置完成,以后不会再出现了:

git config --global pull.rebase false

千鸟语
609 声望24 粉丝

前端开发多年,正在努力向全栈方向拓展